﻿#include "drawview.h"
#include "drawscene.h"
#include "globaldata.h"
#include "propertiesdlg.h"
#include <QSvgGenerator>
#include <QMimeData>
#include <QGraphicsPixmapItem>
#include <QDateTime>
#include <QXmlStreamWriter>
#include <QXmlStreamReader>

DrawView::DrawView(QGraphicsScene *scene)
    :QGraphicsView(scene)
{
    m_hruler = new QtRuleBar(Qt::Horizontal,this,this);
    m_vruler = new QtRuleBar(Qt::Vertical,this,this);
    box = new QtCornerBox(this);
    setViewport(new QWidget);

    //setDragMode(QGraphicsView::NoDrag); // 禁用默认拖动模式
    //setCursor(Qt::ArrowCursor); // 初始光标

    setAttribute(Qt::WA_DeleteOnClose);
    setAcceptDrops(true);
    isUntitled = true;

    modified = false;
}

void DrawView::zoomIn()
{
    scale(1.2,1.2);
    updateRuler();
}

void DrawView::zoomOut()
{
    scale(1 / 1.2, 1 / 1.2);
    updateRuler();
}

void DrawView::newFile()
{
    static int sequenceNumber = 1;

    isUntitled = true;
    curFile = tr("drawing%1.xml").arg(sequenceNumber++);
    setWindowTitle(curFile + "[*]");
}

bool DrawView::loadFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        QMessageBox::warning(this, tr("Qt Drawing"),
                             tr("Cannot read file %1:\n%2.")
                             .arg(fileName)
                             .arg(file.errorString()));
        return false;
    }

    loadCanvas();
    setCurrentFile(fileName);
    return true;
}

bool DrawView::save()
{
    if (isUntitled) {
        return saveAs();
    } else {
        return saveFile(curFile);
    }
}

bool DrawView::saveAs()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
                                                    curFile);
    if (fileName.isEmpty())
        return false;

    return saveFile(fileName);

}

bool DrawView::saveFile(const QString &fileName)
{
    QFile file(fileName);
    if (!file.open(QFile::WriteOnly | QFile::Text))
        return false;
    qDebug() << "save File Fail";

    updateGlobalUnits();
    updateGlobaPinMap();

    const QFileInfo fileInfo(fileName);
    const QString basePath = fileInfo.absoluteDir().absolutePath();

    QXmlStreamWriter xml(&file);
    xml.setAutoFormatting(true);
    xml.writeStartDocument();
    xml.writeDTD("<!DOCTYPE component>");
    xml.writeStartElement("component");
    xml.writeAttribute("id", "1");

    writeUnitsToXml(xml, basePath);
    writeMapConnections(xml);

    xml.writeEndElement();
    xml.writeEndDocument();
    setCurrentFile(fileName);
    return true;
}


QString DrawView::userFriendlyCurrentFile()
{
    return strippedName(curFile);
}

static void setItemRotation(GraphicsRectItem* item, int value)
{
    if(!item)
        return;

    item->setRotation(item->rotation() + value);
    item->updateCoordinate();
    item->updateUnitInfo();
    item->updateConnectLineTool();
}

void DrawView::showContextMenu(GraphicsRectItem *item, const QPoint &pos)
{
    if(panning)
        return;

    QMenu menu;
    QAction* copyAction = menu.addAction("复制");
    QAction* pasteAction = menu.addAction("粘贴");
    //QAction* leftRotateAction = menu.addAction("左旋90度");
    //QAction* rightRotateAction = menu.addAction("右旋90度");
    QAction* propertiesAction = menu.addAction("属性");

    if(!item)
    {
        copyAction->setEnabled(false);
        //leftRotateAction->setEnabled(false);
        //rightRotateAction->setEnabled(false);
        propertiesAction->setEnabled(false);
    }

    connect(copyAction, &QAction::triggered,[=](){
        emit signalMenuCopy();
    });
    connect(pasteAction, &QAction::triggered,[=](){
        emit signalMenuPaste();
    });
    connect(propertiesAction, &QAction::triggered,[=](){
        showProperties(item, QString("属性"));
    });
//    connect(leftRotateAction, &QAction::triggered,[=](){
//        setItemRotation(item, 90);
//    });
//    connect(rightRotateAction, &QAction::triggered,[=](){
//        setItemRotation(item, -90);
//    });
    menu.exec(pos);
}

void DrawView::showProperties(GraphicsRectItem *item, const QString &string)
{
    PropertiesDlg propertiesDlg(item, this);
    propertiesDlg.setWindowTitle(string);
    propertiesDlg.exec();
}

void DrawView::closeEvent(QCloseEvent *event)
{
    if (maybeSave())
        event->accept();
    else
        event->ignore();
}

void DrawView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton && (event->modifiers() & Qt::ControlModifier))
    {
        panning = true;
        lastPanPos = event->pos(); // 记录初始鼠标位置
        setCursor(Qt::ClosedHandCursor); // 设置光标为手型
        event->accept(); // 接受事件，防止传递
        return;
    }
    QGraphicsView::mousePressEvent(event); // 其他情况交基类处理
}

void DrawView::mouseMoveEvent(QMouseEvent *event)
{
    if (panning)
    {
        // 计算鼠标移动的增量
        QPoint delta = event->pos() - lastPanPos;
        // 调整滚动条的位置
        horizontalScrollBar()->setValue(horizontalScrollBar()->value() - delta.x());
        verticalScrollBar()->setValue(verticalScrollBar()->value() - delta.y());
        lastPanPos = event->pos(); // 更新记录的位置
        event->accept();
        return;
    }
    QPointF pt =mapToScene(event->pos());
    m_hruler->updatePosition(event->pos());
    m_vruler->updatePosition(event->pos());
    emit positionChanged( pt.x() , pt.y() );
    QGraphicsView::mouseMoveEvent(event);
}

void DrawView::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton && panning)
    {
        panning = false;
        setCursor(Qt::ArrowCursor); // 恢复光标
        event->accept();
        return;
    }
    QGraphicsView::mouseReleaseEvent(event);
}

void DrawView::resizeEvent(QResizeEvent *event)
{
    QGraphicsView::resizeEvent(event);

    this->setViewportMargins(RULER_SIZE-1,RULER_SIZE-1,0,0);
    m_hruler->resize(this->size().width()- RULER_SIZE - 1,RULER_SIZE);
    m_hruler->move(RULER_SIZE,0);
    m_vruler->resize(RULER_SIZE,this->size().height() - RULER_SIZE - 1);
    m_vruler->move(0,RULER_SIZE);

    box->resize(RULER_SIZE,RULER_SIZE);
    box->move(0,0);
    updateRuler();
}

void DrawView::scrollContentsBy(int dx, int dy)
{
    QGraphicsView::scrollContentsBy(dx,dy);
    updateRuler();
}

void DrawView::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat("image/png"))
        event->acceptProposedAction();
}

void DrawView::dropEvent(QDropEvent *event)
{
    const QMimeData* mimeData = event->mimeData();
    QPixmap pixmap;
    pixmap.loadFromData(mimeData->data("image/png"), "PNG");

    if (!pixmap.isNull())
    {
        // 转换坐标到场景坐标系
        QPoint viewPos = event->pos();
        QPointF scenePos = mapToScene(viewPos);

        // 添加图片到场景
        QGraphicsPixmapItem* item = scene()->addPixmap(pixmap);
        item->setPos(scenePos);
        event->acceptProposedAction();
    }
}

void DrawView::updateRuler()
{
    if ( scene() == 0) return;
    QRectF viewbox = this->rect();
    QPointF offset = mapFromScene(scene()->sceneRect().topLeft());
    double factor =  1./transform().m11();
    double lower_x = factor * ( viewbox.left()  - offset.x() );
    double upper_x = factor * ( viewbox.right() -RULER_SIZE- offset.x()  );
    m_hruler->setRange(lower_x,upper_x,upper_x - lower_x );
    m_hruler->update();

    double lower_y = factor * ( viewbox.top() - offset.y()) * -1;
    double upper_y = factor * ( viewbox.bottom() - RULER_SIZE - offset.y() ) * -1;

    m_vruler->setRange(lower_y,upper_y,upper_y - lower_y );
    m_vruler->update();
}

bool DrawView::maybeSave()
{
    if (isModified()) {
        QMessageBox::StandardButton ret;
        ret = QMessageBox::warning(this, tr("MDI"),
                     tr("'%1' has been modified.\n"
                        "Do you want to save your changes?")
                     .arg(userFriendlyCurrentFile()),
                     QMessageBox::Save | QMessageBox::Discard
                     | QMessageBox::Cancel);
        if (ret == QMessageBox::Save)
            return save();
        else if (ret == QMessageBox::Cancel)
            return false;
    }
    return true;
}

void DrawView::setCurrentFile(const QString &fileName)
{
    curFile = QFileInfo(fileName).canonicalFilePath();
    isUntitled = false;
    setModified(false);
    setWindowModified(false);
    setWindowTitle(userFriendlyCurrentFile() + "[*]");
}

QString DrawView::strippedName(const QString &fullFileName)
{
    return QFileInfo(fullFileName).fileName();
}

void DrawView::loadCanvas()
{
    for(auto it : GlobalData::getInstance().getUnits())
    {
        QPoint pos = QPoint(it.pos.x.toInt(), it.pos.y.toInt());
        QSize unitSize = QSize(it.pos.w.toInt(), it.pos.h.toInt());
        GraphicsRectItem* item = new GraphicsRectItem(QRect(pos, unitSize));
        item->updateCoordinate();
        item->setPixmap(QPixmap(it.iconPath).scaled(unitSize));
        item->setUnit(it);
        item->setRotation(it.pos.angle.toInt());
        item->updatePinPointHandles();
        scene()->addItem(item);
    }
    loadConnectLines();
}

GraphicsRectItem *DrawView::getRectItemFormUnidId(QString unitId)
{
    for(auto it : scene()->items())
    {
        GraphicsRectItem* item = dynamic_cast<GraphicsRectItem*>(it);
        if(!item)
            continue;
        if(item->getUnit().id == unitId)
            return item;
    }
    return nullptr;
}

void DrawView::loadConnectLines()
{
    for(auto it : GlobalData::getInstance().getPinMap())
    {
        GraphicsRectItem* startItem = getRectItemFormUnidId(it.first.unitId);
        GraphicsRectItem* endItem = getRectItemFormUnidId(it.second.unitId);
        if(!startItem || !endItem)
            continue;
        PinPointHandle* startPinHandle = startItem->getPinFormPinIndex(it.first.index);
        PinPointHandle* endPinHandle = endItem->getPinFormPinIndex(it.second.index);
        if(!startPinHandle || !endPinHandle)
            continue;

        GraphicsConnectLineItem* item = new GraphicsConnectLineItem(startItem);
        item->getPivotTool()->setStartPin(startPinHandle);
        item->getPivotTool()->setStartPoint(startPinHandle->getPos());
        item->getPivotTool()->setEndPin(endPinHandle);
        item->getPivotTool()->setEndPoint(startItem->mapFromScene(endPinHandle->scenePos()));

        item->getPivotTool()->calculatePivotPoints(startPinHandle, item->getPivotTool()->getEndPoint());
        item->initMidRects();
        startItem->startConnectLineItems->append(item);
        endItem->startConnectLineItems->append(item);
        item->show();
    }
}

void DrawView::updateGlobalUnits()
{
    GlobalData::getInstance().getUnits().clear();
    for(auto it : scene()->items())
    {
        GraphicsRectItem* item = dynamic_cast<GraphicsRectItem*>(it);
        if(item)
        {
            GlobalData::getInstance().getUnits().append(item->getUnit());
        }
    }
}

void DrawView::updateGlobaPinMap()
{
    GlobalData::getInstance().getPinMap().clear();
    for(auto it : scene()->items())
    {
        GraphicsConnectLineItem* item = dynamic_cast<GraphicsConnectLineItem*>(it);
        if(item && item->getPivotTool()->getStartPin() && item->getPivotTool()->getEndPin())
        {
            std::pair<Pin, Pin> pinMap(item->getPivotTool()->getStartPin()->getPin(), item->getPivotTool()->getEndPin()->getPin());
            GlobalData::getInstance().getPinMap().append(pinMap);
        }
    }
}

void DrawView::writeUnitsToXml(QXmlStreamWriter &xml, const QString &basePath)
{
    xml.writeStartElement("units");
    const auto& units = GlobalData::getInstance().getUnits();
    for (const Unit& unit : units)
        writeUnitElement(xml, unit, basePath);
    xml.writeEndElement();
}

static void writePinElement(QXmlStreamWriter &xml, const Pin &pin)
{
    xml.writeStartElement("pin");
    xml.writeAttribute("index", QString::number(pin.index));
    xml.writeAttribute("name", pin.name);
    xml.writeAttribute("unit", pin.unit);
    xml.writeAttribute("desc", pin.desc);
    xml.writeAttribute("dir", pin.dir);
    xml.writeAttribute("value", pin.value);
    xml.writeEndElement();
}

static void writeConstautElement(QXmlStreamWriter &xml, const Unit::Constaut &constant)
{
    xml.writeStartElement("constant");
    xml.writeAttribute("index", QString::number(constant.index));
    xml.writeAttribute("name", constant.name);
    xml.writeAttribute("value", constant.value);
    xml.writeAttribute("unit", constant.unit);
    xml.writeAttribute("desc", constant.desc);
    xml.writeEndElement();
}

void DrawView::writeUnitElement(QXmlStreamWriter &xml, const Unit &unit, const QString &basePath)
{
    xml.writeStartElement("unit");
    xml.writeAttribute("id", unit.id);
    xml.writeAttribute("type", unit.type);
    xml.writeAttribute("name", unit.name);
    xml.writeAttribute("desc", unit.desc);

    QString relativeIconPath = QDir(basePath).relativeFilePath(unit.iconPath);
    xml.writeStartElement("pos");
    xml.writeAttribute("x", unit.pos.x);
    xml.writeAttribute("y", unit.pos.y);
    xml.writeAttribute("w", unit.pos.w);
    xml.writeAttribute("h", unit.pos.h);
    xml.writeAttribute("angle", unit.pos.angle);
    xml.writeAttribute("icon", relativeIconPath);
    xml.writeEndElement();

    xml.writeStartElement("input");
    for (const Pin& pin : unit.inputPins)
        writePinElement(xml, pin);
    xml.writeEndElement();

    xml.writeStartElement("output");
    for (const Pin& pin : unit.outputPins)
        writePinElement(xml, pin);
    xml.writeEndElement();

    xml.writeStartElement("code");
    xml.writeAttribute("type", unit.codeType);
    xml.writeAttribute("value", unit.codeValue);
    xml.writeEndElement();

    xml.writeStartElement("constants");
    for (const Unit::Constaut& constant : unit.constants)
        writeConstautElement(xml, constant);
    xml.writeEndElement();

    xml.writeEndElement();
}

void DrawView::writeMapConnections(QXmlStreamWriter &xml)
{
    xml.writeStartElement("map");

    const auto& pinMap = GlobalData::getInstance().getPinMap();
    for (const auto& connection : pinMap) {
        writeConnectionElement(xml, connection.first, connection.second);
    }

    xml.writeEndElement();
}

void DrawView::writeConnectionElement(QXmlStreamWriter &xml, const Pin &fromPin, const Pin &toPin)
{
    xml.writeStartElement("connect");
    xml.writeAttribute("from", fromPin.unitId);
    xml.writeAttribute("frompin", QString::number(fromPin.index));
    xml.writeAttribute("to", toPin.unitId);
    xml.writeAttribute("topin", QString::number(toPin.index));
    xml.writeEndElement();
}

